#ifndef __DS3231_HPP
#define __DS3231_HPP
#include <Arduino.h>
#include <Wire.h>
#include<stdio.h>
#include "SerialDebug.hpp"
/*
  0x57为EEPROM的地址
  104(0x68)为DS3231的地址
*/

/*
 * AUTHOR:FIRFIT AKARIN
 * DATE:2021.NOV11
 * DS3231适配库文件-Arduino
 * 格式声明：
 * HSB^-x为以最高位向右x位的位置
 * LSB^-x为以最低位向左x位的位置
*/

/*主机地址*/
#define ADDRDS3231    104
#define ADDR24C32     0X57


/*DS3231各个寄存器的位置（若写时，需要将该地址与上一个）*/
#define RTC_SECONDS   0X00//秒
#define RTC_MINUTES   0X01//分
#define RTC_HOURS     0X02//时
#define RTC_WEEK      0X03//星期
#define RTC_DAY       0X04//日
#define RTC_MONTH     0X05//月
#define RTC_YEAR      0X06//年

#define RTC_ALM1SEC   0X07//闹铃1：秒设置
#define RTC_ALM1MIN   0X08//闹铃1：分设置
#define RTC_ALM1HOR   0X09//闹铃1：时设置
#define RTC_ALM1WEDA  0X0A//闹铃1：当BIT6为0时，从BIT0~BIT3表示的是日期，当BIT6为1时，从BIT0到BIT3表示的是星期

#define RTC_ALM2MIN   0X0B
#define RTC_ALM2HOR   0X0C
#define RTC_ALM2WEDA  0X0D

#define RTC_CTRL      0X0E//DS3231的控制位
#define RTC_STAT      0X0F//DS3231的状态位
#define RTC_AGOF      0X10//
#define RTC_TEPM      0X11//DS3231读取到的温度——高八位，其中最高位为标志位
#define RTC_TEML      0X12//DS3231读取到的温度——低两位，剩下的6位无效

/*DS3231控制位定义*/


typedef enum RTC_CONTROLBIT//RTC_CTRL寄存器的位定义，若想要访问或写入某一个位，请使用位运算符
{
  RTC_CTRL_EOSC=0X80,//使能内部晶振
  RTC_CTRL_BBSQW=0X40,//允许通过INTCN输出1HZ的信号
  RTC_CTRL_CONV=0X20,//温度转换控制位（将原本的温度数据转换为8421二进制码），1有效，当转换完成后，该位置为0
  RTC_CTRL_INTCN=0X04,//总中断使能（即允许任何中断通过INT（部分板子可能脚SQW）针脚输出脉冲信号）
  RTC_CTRL_A2IE=0X02,//开启闹铃2的输出中断_同INTCN一同使用
  RTC_CTRL_A1IE=0X01//开启闹铃2的输出中断_同INTCN一同使用
  };

typedef struct
{
  byte HOUR;
  byte MIN;
  byte SEC;
  byte day;
  byte month;
  byte week;
  byte year;
  byte TEMPREH;//8位
  byte TEMPREL;//8位
  float TEMPDATA;//32位
  bool Is24HOUR;
}timeSET;//设置时间的结构体类型

/*
 * 使用寄存器位置来访问某一个位的方式
 * 例：访问EOSC位
 * Wire.beginTransmission(ADDRDS3231);
 * Wire.write(RTC_CTRL);
 * byte getDataOfCTRL=Wire.read();//获取到CTRL寄存器的所有值
 * 
 * getValOfEOSE=getDataOfCTRL&RTC_CTRL_EOSC;//对不是目标位的其他位与上一个0，仅目标位为1
 * bool ValEOSC=bool(getValOfEOSE);//若该位为1，则getValOfEOSE为0X80，否则为0，将其转换为布尔类型即可
*/








byte recFromRTC(byte RegADDR)//从DS3231接收数据
{
    byte temp=0;
    Wire.beginTransmission(ADDRDS3231);
    Wire.write(RegADDR);
    Wire.endTransmission();
    temp=Wire.requestFrom(ADDRDS3231,1);
    temp=Wire.read();
    return temp;
  }

void TransToRTC(byte regADDR,byte param)//regADDR:DS3231的寄存器，param为操作数
{
  byte DS3231WR=ADDRDS3231|0x80;
  Wire.beginTransmission(DS3231WR);
  Wire.write(regADDR);
  Wire.write(param);
  Wire.endTransmission();
  }

void SETTIME(timeSET* OPCODE)//设置时间
{
  byte SECPARAM=(((OPCODE->SEC)/10)<<4)|(OPCODE->SEC)%10;//DS3231的秒寄存器存放数据的格式遵循8421码元
  byte MINPARAM=(((OPCODE->MIN)/10)<<4)|(OPCODE->MIN)%10;//DS3231的寄存器数据格式同上
  byte HORPARAM;
  byte YEARPARAM=(((OPCODE->year)/10)<<4)|(OPCODE->year)%10;
  byte WEEKPARAM=OPCODE->week;
  byte DAYPARAM=(((OPCODE->day)/10)<<4)|(OPCODE->day)%10;
  byte MONTHPARAM=(((OPCODE->month)/10)<<4)|(OPCODE->month)%10;
  if(OPCODE->Is24HOUR==true)//如果设置为24小时显示方式
  {
    HORPARAM|=0X40;//HSB^-2为设置是否为24小时制
    HORPARAM=((OPCODE->HOUR/10)<<4)|(OPCODE->HOUR%10);//由于HSB^-2是设置2X小时 HSB^-3是设置1X小时，所以对于24小时的处理，只需要将数据处理为8421码元分别存入即可
  }
  else
    HORPARAM=((OPCODE->HOUR/10)<<4)|(OPCODE->HOUR%10);

  TransToRTC(RTC_SECONDS,SECPARAM);
  TransToRTC(RTC_MINUTES,MINPARAM);
  TransToRTC(RTC_HOURS,HORPARAM);
  
  TransToRTC(RTC_WEEK,WEEKPARAM);
  TransToRTC(RTC_DAY,DAYPARAM);
  TransToRTC(RTC_MONTH,MONTHPARAM);
  TransToRTC(RTC_YEAR,YEARPARAM);  
  /*从这段开始为测试代码，若正式使用时可以将其注释:注释和取消注释操作:右键选中CTRL+左斜杠键，若想取消注释，则可再重复该操作*/
//  Serial.println("PARAM STATUS");
//  Serial.print(HORPARAM,HEX);Serial.print(' ');
//  Serial.print(MINPARAM,HEX);Serial.print(' ');
//  Serial.print(SECPARAM,HEX);Serial.print(' ');
//
//  Serial.print(YEARPARAM,HEX);Serial.print(' ');
//  Serial.print(MONTHPARAM,HEX);Serial.print(' ');
//  Serial.print(DAYPARAM,HEX);Serial.print(' ');
//  Serial.print(WEEKPARAM,HEX);Serial.print('\n');
                                          /*TEST CODE END*/
}

timeSET RECTIME()//将接收的数据处理返回
{
  timeSET RESULT={0,0,0,0,0,0,0,0,0};//初始化结构体
  byte RecFromDS3231[20]={};//接收的原始数据
  for(int i=0;i<20;i++)
  {
    RecFromDS3231[i]=recFromRTC(i);
    }
  RESULT.SEC=((RecFromDS3231[0]&0xf0)>>4)*10+(RecFromDS3231[0]&0x0f);
  RESULT.MIN=((RecFromDS3231[1]&0xf0)>>4)*10+(RecFromDS3231[1]&0x0f);
  RESULT.HOUR=((RecFromDS3231[2]&0x30)>>4)*10+(RecFromDS3231[2]&0x0f);
  
  RESULT.week=RecFromDS3231[3];
  RESULT.day=((RecFromDS3231[4]&0x30)>>4)*10+(RecFromDS3231[4]&0x0f);
  RESULT.month=((RecFromDS3231[5]&0x10)>>4)*10+(RecFromDS3231[5]&0x0f);
  RESULT.year=((RecFromDS3231[6]&0xf0)>>4)*10+(RecFromDS3231[6]&0x0f);
  RESULT.TEMPREH=RecFromDS3231[17];
  RESULT.TEMPREL=RecFromDS3231[18]>>6;
  RESULT.TEMPDATA=((RecFromDS3231[17]&0x7f)+(RecFromDS3231[18]>>6)*0.25);
  if((RecFromDS3231[17]&0x80)==0x80)
  RESULT.TEMPDATA*=(-1.0);
  return RESULT;
  }

void ENAB_TEMP()//激活温度转换功能
{
  byte ORIG_RTC_CTRL=recFromRTC(RTC_CTRL);//备份CTRL寄存器的数据
  ORIG_RTC_CTRL|=RTC_CTRL_CONV;
  TransToRTC(RTC_CTRL,ORIG_RTC_CTRL);//写入控制寄存器
  while((recFromRTC(RTC_CTRL)&RTC_CTRL_CONV)==RTC_CTRL_CONV);
  }

void DebugDS3231BYSERIAL()//通过串口调试DS3231
{
  char RECT[50];
  Serial.println("Please input your COMMAND");
  while(Serial.available()==0);
  delay(150);
    for(int i=0;i<50;i++)
    {
      RECT[i]=Serial.read();//从串口读取数据
    }
  
  
  if(RECT[0]=='S' && RECT[1]=='E' && RECT[2]=='T')//设置时间 指令格式SET 2021-11-20 18:23 6(年份-月份-日期 时:分 星期)
  {
    timeSET RECD;
    RECD.year=(RECT[6]-0x30)*10+(RECT[7]-0x30);
    RECD.month=(RECT[9]-0x30)*10+(RECT[10]-0x30);
    RECD.day=(RECT[12]-0x30)*10+(RECT[13]-0x30);
    RECD.HOUR=(RECT[15]-0x30)*10+(RECT[16]-0x30);
    RECD.MIN=(RECT[18]-0x30)*10+(RECT[19]-0x30);
    RECD.week=(RECT[21]-0x30);
    SETTIME(&RECD);

    Serial.print(RECD.year);
    Serial.print('-');
    Serial.print(RECD.month);
    Serial.print('-');
    Serial.print(RECD.day);
    Serial.print(' ');
    Serial.print(RECD.HOUR);
    Serial.print(':');
    Serial.print(RECD.MIN);
    Serial.println(RECD.week);  
    }

    if(RECT[0]=='C' && RECT[1]=='H' && RECT[2]=='K')//获取DS3231的时间数据和温度数据
    {
      timeSET initi;
      initi=RECTIME();
      ENAB_TEMP();
      Serial.print(initi.HOUR);
      Serial.print(':');
      Serial.print(initi.MIN);
      Serial.print(':');
      Serial.println(initi.SEC);

      Serial.print(initi.year);
      Serial.print('-');
      Serial.print(initi.month);
      Serial.print('-');
      Serial.print(initi.day);
      Serial.print('-');
      Serial.println(initi.week);

      Serial.println(initi.TEMPDATA); 
    }
    if(RECT[0]=='S' && RECT[1]=='C' && RECT[2]=='A')//扫描器件I2C地址
    scanIIC();
    
  }

  
#endif
